#include "jdatastream.h"
#include <JXQIO/JAbstractIO>

namespace JXQ
{

typedef JDataStream::Buffer  Buffer;

class J_DECL_HIDDEN JDataStreamPrivate
{
public:
	JDataStreamPrivate(JAbstractIO *io);
	JDataStreamPrivate(Buffer &buffer);
	~JDataStreamPrivate();

public:
	void read(void *mem, int size);

public:
	std::list<uint64_t> *buffer;
	JAbstractIO *ioDev;
};

JDataStreamPrivate::JDataStreamPrivate(JAbstractIO *io)
{
	if( io == nullptr )
		buffer = new std::list<uint64_t>();
	else
	{
		ioDev = io;
		buffer = nullptr;
	}
}

JDataStreamPrivate::JDataStreamPrivate(Buffer &buffer) :
	buffer(&buffer)
{
	ioDev = nullptr;
}

JDataStreamPrivate::~JDataStreamPrivate()
{
	if( buffer != nullptr )
		delete buffer;
}

void JDataStreamPrivate::read(void *mem, int size)
{
	if( buffer )
	{
		if( not buffer->empty() )
		{
			memcpy(mem, &buffer->front(), size);
			buffer->pop_back();
		}
	}
	else
	{

	}
}

/*----------------------------------------------------------------------------------*/

JDataStream::JDataStream(Buffer &buffer) :
	d_ptr(new JDataStreamPrivate(buffer))
{

}

JDataStream::JDataStream(JAbstractIO *io) :
	d_ptr(new JDataStreamPrivate(io))
{

}

JDataStream::JDataStream(const JDataStream &other) :
	d_ptr(new JDataStreamPrivate(nullptr))
{
	d_ptr->ioDev = other.d_ptr->ioDev;
	*d_ptr->buffer = *other.d_ptr->buffer;
}

JDataStream::JDataStream(JDataStream &&other) :
	d_ptr(other.d_ptr)
{
	other.d_ptr = nullptr;
}

JDataStream::~JDataStream()
{
	if( d_ptr != nullptr )
		delete d_ptr;
}

const JDataStream &JDataStream::operator=(const JDataStream &other)
{
	d_ptr->ioDev = other.d_ptr->ioDev;
	if( other.d_ptr->buffer == nullptr )
	{
		delete d_ptr->buffer;
		d_ptr->buffer = nullptr;
	}
	else
	{
		if( d_ptr->buffer == nullptr )
			d_ptr->buffer = new Buffer(*other.d_ptr->buffer);
		else
			*d_ptr->buffer = *other.d_ptr->buffer;
	}
	return *this;
}

const JDataStream &JDataStream::operator=(JDataStream &&other)
{
	d_ptr = other.d_ptr;
	other.d_ptr = nullptr;
	return *this;
}

JDataStream &JDataStream::operator<<(signed char data)
{
	return operator<<(J_SCT(long long, data));
}

JDataStream &JDataStream::operator<<(unsigned char data)
{
	return operator<<(J_SCT(unsigned long long, data));
}

JDataStream &JDataStream::operator<<(short data)
{
	return operator<<(J_SCT(long long, data));
}

JDataStream &JDataStream::operator<<(unsigned short data)
{
	return operator<<(J_SCT(unsigned long long, data));
}

JDataStream &JDataStream::operator<<(int data)
{
	return operator<<(J_SCT(long long, data));
}

JDataStream &JDataStream::operator<<(unsigned int data)
{
	return operator<<(J_SCT(unsigned long long, data));
}

JDataStream &JDataStream::operator<<(long data)
{
	return operator<<(J_SCT(long long, data));
}

JDataStream &JDataStream::operator<<(unsigned long data)
{
	return operator<<(J_SCT(unsigned long long, data));
}

JDataStream &JDataStream::operator<<(long long data)
{
	return operator<<(J_SCT(unsigned long long, data));
}

JDataStream &JDataStream::operator<<(unsigned long long data)
{
	if( d_ptr->buffer )
		d_ptr->buffer->emplace_back(data);
	else
	{

	}
	return *this;
}

JDataStream &JDataStream::operator<<(float data)
{
	return operator<<(J_SCT(long double, data));
}

JDataStream &JDataStream::operator<<(double data)
{
	return operator<<(J_SCT(long double, data));
}

JDataStream &JDataStream::operator<<(long double data)
{
	if( d_ptr->buffer )
	{
		d_ptr->buffer->emplace_back(0);
		memcpy(&d_ptr->buffer->back(), &data, sizeof(data));
	}
	else
	{

	}
	return *this;
}

JDataStream &JDataStream::operator<<(char *str)
{
	int i = 0;
	while( str[i] != '\0' )
		operator<<(str[i++]);
	return operator<<(str[i]);
}

JDataStream &JDataStream::operator<<(const char *str)
{
	int i = 0;
	while( str[i] != '\0' )
		operator<<(str[i++]);
	return operator<<(str[i]);
}

JDataStream &JDataStream::operator>>(signed char &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(unsigned char &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(short &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(unsigned short &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(int &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(unsigned int &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(long &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(unsigned long &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(long long &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(unsigned long long &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(float &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(double &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(long double &data)
{
	d_ptr->read(&data, sizeof(data));
	return *this;
}

JDataStream &JDataStream::operator>>(char *str)
{
	if( str != nullptr )
	{
		int i = 0;
		auto it = d_ptr->buffer->begin();

		while( it != d_ptr->buffer->end() and  *it != '\0' )
		{
			str[i++] = J_SCT(char, *it);
			it = d_ptr->buffer->erase(it);
		}
	}
	return *this;
}

} // namespace JXQ
